package org.apache.fop.layoutmgr.inline;

import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.apache.fop.area.Area;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FOText;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.properties.CommonFont;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.LayoutManagerMapping;
import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.LayoutManagerMapping.Maker;
import org.apache.fop.layoutmgr.inline.TextLayoutManager;

import com.thunisoft.converter.ConverterUtils;

public class CustomTextLayoutManager extends TextLayoutManager {

    public static int TRAIT_ORIGIN_FONT = 101;

    /**
     * ԭʼFont
     */
    private FontTriplet originFont;

    public CustomTextLayoutManager(FOText node) {
        super(node);
        this.parseOriginFont(node);
    }

    private void parseOriginFont(FOText node) {
        CommonFont fontProps = node.getCommonFont();
        int fw;
        switch (fontProps.getFontWeight()) {
        case Constants.EN_100:
            fw = 100;
            break;
        case Constants.EN_200:
            fw = 200;
            break;
        case Constants.EN_300:
            fw = 300;
            break;
        case Constants.EN_400:
            fw = 400;
            break;
        case Constants.EN_500:
            fw = 500;
            break;
        case Constants.EN_600:
            fw = 600;
            break;
        case Constants.EN_700:
            fw = 700;
            break;
        case Constants.EN_800:
            fw = 800;
            break;
        case Constants.EN_900:
            fw = 900;
            break;
        default:
            fw = 400;
        }

        String style;
        switch (fontProps.getFontStyle()) {
        case Constants.EN_ITALIC:
            style = "italic";
            break;
        case Constants.EN_OBLIQUE:
            style = "oblique";
            break;
        case Constants.EN_BACKSLANT:
            style = "backslant";
            break;
        default:
            style = "normal";
        }

        this.originFont = new FontTriplet(fontProps.getFirstFontFamily(), style, fw);
    }

    public void setParent(LayoutManager lm) {
        lm = new LayoutManagerProxy(lm);
        super.setParent(lm);
    }

    public LayoutManager getParent() {
        return super.getParent();
    }

    @SuppressWarnings("unchecked")
    public static void registerMaker(LayoutManagerMapping mapping) {
        Map makers = (Map) ConverterUtils.getField(LayoutManagerMapping.class, mapping, "makers");
        makers.put(FOText.class, new FOTextLayoutManagerMaker());
    }

    public static class FOTextLayoutManagerMaker extends Maker {

        @SuppressWarnings("unchecked")
        public void make(FONode node, List lms) {
            FOText foText = (FOText) node;
            if (foText.length() > 0) {
                lms.add(new CustomTextLayoutManager(foText));
            }
        }
    }

    @SuppressWarnings("unchecked")
    private class LayoutManagerProxy implements LayoutManager {

        private LayoutManager target;

        public LayoutManagerProxy(LayoutManager lm) {
            this.target = lm;
        }

        public void addAreas(PositionIterator posIter, LayoutContext context) {
            target.addAreas(posIter, context);
        }

        public void addChildArea(Area childArea) {
            childArea.addTrait(TRAIT_ORIGIN_FONT, originFont);
            target.addChildArea(childArea);
        }

        public void addChildLM(LayoutManager lm) {
            target.addChildLM(lm);
        }

        public void addChildLMs(List newLMs) {
            target.addChildLMs(newLMs);
        }

        public boolean createNextChildLMs(int pos) {
            return target.createNextChildLMs(pos);
        }

        public List getChangedKnuthElements(List oldList, int alignment) {
            return target.getChangedKnuthElements(oldList, alignment);
        }

        public List getChildLMs() {
            return target.getChildLMs();
        }

        public int getContentAreaBPD() {
            return target.getContentAreaBPD();
        }

        public int getContentAreaIPD() {
            return target.getContentAreaIPD();
        }

        public FObj getFObj() {
            return target.getFObj();
        }

        public boolean getGeneratesBlockArea() {
            return target.getGeneratesBlockArea();
        }

        public boolean getGeneratesLineArea() {
            return target.getGeneratesLineArea();
        }

        public boolean getGeneratesReferenceArea() {
            return target.getGeneratesReferenceArea();
        }

        public List getNextKnuthElements(LayoutContext context, int alignment) {
            return target.getNextKnuthElements(context, alignment);
        }

        public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
                Position positionAtIPDChange, LayoutManager restartAtLM) {
            return target.getNextKnuthElements(context, alignment, lmStack, positionAtIPDChange, restartAtLM);
        }

        public PageSequenceLayoutManager getPSLM() {
            return target.getPSLM();
        }

        public LayoutManager getParent() {
            return target.getParent();
        }

        public Area getParentArea(Area childArea) {
            return target.getParentArea(childArea);
        }

        public void initialize() {
            target.initialize();
        }

        public boolean isFinished() {
            return target.isFinished();
        }

        public boolean isRestartable() {
            return target.isRestartable();
        }

        public Position notifyPos(Position pos) {
            return target.notifyPos(pos);
        }

        public void reset() {
            target.reset();
        }

        public void setFinished(boolean isFinished) {
            target.setFinished(isFinished);
        }

        public void setParent(LayoutManager lm) {
            target.setParent(lm);
        }

        public int getBaseLength(int lengthBase, FObj fobj) {
            return target.getBaseLength(lengthBase, fobj);
        }

    }
}
